package com.puzzlebazar.client.puzzle.model;

import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.puzzlebazar.client.puzzleinfo.NewPuzzleInfo;
import com.puzzlebazar.client.puzzleinfo.PuzzleInfo;
import com.puzzlebazar.client.services.PuzzleServiceAsync;
import com.puzzlebazar.client.services.ServiceFactory;
import com.puzzlebazar.client.util.AsyncCallbackLock;
import com.puzzlebazar.client.util.ConnectionLock;
import com.puzzlebazar.client.util.ObservableImpl;
import com.puzzlebazar.client.util.Vec2i;

public abstract class PuzzleModelImpl extends ObservableImpl implements PuzzleModel {

	/**
	 * 
	 */
	private static final long serialVersionUID = -7721465684708392304L;
	
	
	private transient boolean isSaved = true;
	private String puzzleInfoKey = null;
	private Vec2i size = new Vec2i(-1,-1);

	private String message = null;
	
	@Override
	public void create( NewPuzzleInfo newPuzzleInfo ) {
	
		try {
			setGridSize( Vec2i.createFromSizeString( newPuzzleInfo.getSize() ) );
		} catch (Exception e) {
			e.printStackTrace();
			Window.alert( "Unable to create puzzle!" );
		}
		PuzzleServiceAsync puzzleService = ServiceFactory.getPuzzleService();
		puzzleService.createNewPuzzleOrTutorial( newPuzzleInfo,
				new AsyncCallback<String>(){
					public void onFailure(Throwable caught) {
						Window.alert( "Unable to create puzzle!" );
					}
					public void onSuccess(String result) {
						puzzleInfoKey = result;
					} });	
	}

	@Override
	public void attach( PuzzleInfo puzzleInfo ) {

		assert( puzzleInfoKey == null );
		puzzleInfoKey = puzzleInfo.getKey();
		assert(puzzleInfoKey != null);
		try {
			setGridSize( Vec2i.createFromSizeString( puzzleInfo.getSize() ) );
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	@Override
	public String getPuzzleInfoKey() {
		return puzzleInfoKey;
	}
	
	@Override
	public  Vec2i getSize() {
		return size;
	}

	/**
	 * Sets a new size for the grid. Should be called every time a 
	 * resize or expand operation is performed by a child class.
	 * Caller should notify observer eventually
	 * 
	 * @param size The new number of columns / rows
	 * @throws Exception
	 */
	protected void setGridSize( Vec2i gridSize ) throws Exception {
		if( gridSize.equals( size ) ) return;

		if( gridSize.x < 0 || gridSize.y < 0 )
			throw new Exception("Invalid grid size" );

		size.copy( gridSize );
		setChanged();
	}	

	@Override
	public boolean isSaved() {
		return isSaved;
	}

	@Override
	public Move savePuzzle( ConnectionLock lock ) {

		if( puzzleInfoKey == null )  {
			Window.alert( "Puzzle not yet created. Try again later!" );
			return new MoveSavePuzzle( MoveImpl.NO_TIME );
		}
		
		PuzzleServiceAsync puzzleService = ServiceFactory.getPuzzleService();
		puzzleService.savePuzzleModel( 
				this,
				new AsyncCallbackLock<Boolean>(lock){
					@Override
					public void onFailure(Throwable caught) {
						super.onFailure(caught);
						setChanged();
						isSaved = false;
						notifyObservers( new MoveSavePuzzleFailed( MoveImpl.NO_TIME ) );
						Window.alert( "Save failed!" );
					}
					@Override
					public void onSuccess(Boolean result) {
						super.onSuccess(result);
					} });
	
		setChanged();
		isSaved = true;
		return new MoveSavePuzzle( MoveImpl.NO_TIME );
	}

	@Override
	public MoveList getMoveList() {
		MoveList moveList = new MoveList(MoveImpl.NO_TIME);		
		moveList.addMove( new MoveMessage(message,null,MoveImpl.NO_TIME) );
		return moveList;
	}	

	@Override
	public void setChanged() {
		super.setChanged();
		isSaved = false;
	}
	

	@Override
	public MoveMessage setMessage(String message, long time) {

		MoveMessage move = new MoveMessage(message, this.message, time);
		this.message = message;
		
		setChanged();

		return move;
	}

	@Override
	public String getMessage() {
		return message;
	}
	
}
